/**
 * \file
 * \brief
 *      This file contains functions for testing on a live device
 * \copyright
 *  Copyright (c) 2022 - 2023 Qualcomm Technologies, Inc.
 *  All Rights Reserved.
 *  Confidential and Proprietary - Qualcomm Technologies, Inc.
 */

#ifndef DISABLE_TEST_API

#include "ar_test.h"

#include "ar_osal_file_io.h"
#include "ar_osal_heap.h"
#include "ar_osal_error.h"
#include "acdb.h"
#include "gsl_intf.h"

#include <logging.h>
#include <panic.h>

static uint32_t *acdb_buf;

typedef struct gsl_key_vector gsl_key_vector_t;
typedef struct gsl_key_value_pair gsl_key_value_pair_t;

#define ACDB_FILENAME       "acdb_cal.acdb"
#define ACDB_FILENAME_LEN   (sizeof(ACDB_FILENAME) - 1)

static ar_heap_info heap_info = {
    .align_bytes = AR_HEAP_ALIGN_DEFAULT,
    .pool_type = AR_HEAP_POOL_DEFAULT,
    .heap_id = AR_HEAP_ID_DEFAULT,
    .tag = AR_HEAP_TAG_DEFAULT
};

UNITCODESECTION(KEEP)
gsl_key_vector_t *ArTestGkvAlloc(uint32_t num_kvps)
{
    gsl_key_vector_t *gkv = ar_heap_calloc(sizeof(gsl_key_vector_t), &heap_info);
    gkv->kvp = ar_heap_calloc(sizeof(gsl_key_value_pair_t) * num_kvps, &heap_info);
    gkv->num_kvps = num_kvps;

    return gkv;
}

UNITCODESECTION(KEEP)
void ArTestGkvFree(gsl_key_vector_t *gkv)
{
    ar_heap_free(gkv->kvp, &heap_info);
    ar_heap_free(gkv, &heap_info);
}

UNITCODESECTION(KEEP)
uint32_t ArOsalTestOpenAcdbFile(const char *filename)
{
    DEBUG_LOG("%s", __func__);
    ar_fhandle acdb_handle;
    uint32_t result = 0;
    size_t bytes_read = 0;
    size_t acdb_size = 0;

    result |= ar_fopen(&acdb_handle, filename, AR_FOPEN_READ_ONLY);

    acdb_size = ar_fsize(acdb_handle);
    DEBUG_LOG_ALWAYS("ar_fsize(%p) = %u", acdb_handle, acdb_size);

    acdb_buf = ar_heap_malloc(acdb_size, &heap_info);

    result |= ar_fread(acdb_handle, acdb_buf, acdb_size, &bytes_read);
    DEBUG_LOG_ALWAYS("ar_fread(%p, %p, %u, &bytes_read): bytes_read = %u", acdb_handle, acdb_buf, acdb_size, bytes_read);

    return result;
}

const AcdbFile acdb_file = {
    .fileNameLen = ACDB_FILENAME_LEN,
    .fileName = ACDB_FILENAME,
};

AcdbDataFiles acdb_files = {
    .num_files = 1,
    .acdbFiles = {0}
};

UNITCODESECTION(KEEP)
int32_t AcdbTestLoad(void)
{
    DEBUG_LOG("%s", __func__);
    acdb_files.acdbFiles[0] = acdb_file;
    return acdb_init(&acdb_files, NULL);
}

static struct gsl_acdb_data_files gsl_acdb_files = {
    .num_files = 1,
    .acdbFiles = {
        {.fileNameLen = ACDB_FILENAME_LEN, .fileName = ACDB_FILENAME}
    }
};

static struct gsl_init_data gsl_init_data = {
    .acdb_files = &gsl_acdb_files,
    .acdb_delta_file = NULL,
    .acdb_addr = NULL,
    .max_num_ready_checks = 0,
    .ready_check_interval_ms = 0
};


static bool gsl_init_done = FALSE;

UNITCODESECTION(KEEP)
void GslTestInit(void)
{
    DEBUG_LOG("%s", __func__);
    int32_t result = 0;
    if (!gsl_init_done)
    {
        result = gsl_init(&gsl_init_data);
        DEBUG_LOG("%s result = %u", __func__, result);
        
        if (AR_EOK == result || AR_EALREADY == result)
        {
            gsl_init_done = TRUE;
        }
        else
        {
            DEBUG_LOG("Failure to initialize GSL");
            Panic();
        }
    }
    return;
}

UNITCODESECTION(KEEP)
int32_t GslTestOpen(gsl_key_vector_t *gkv)
{
    DEBUG_LOG("%s", __func__);
    int32_t result = 0;

    GslTestInit();

    gsl_handle_t handle;

    PanicFalse(AR_SUCCEEDED(gsl_open(gkv, NULL, &handle)));

    PanicFalse(AR_SUCCEEDED(gsl_ioctl(handle, GSL_CMD_PREPARE, NULL, 0)));

    return result;
}

UNITCODESECTION(KEEP)
int32_t GslTestBenchmark(gsl_key_vector_t *gkv)
{
    int32_t result = 0;

    GslTestInit();

    gsl_handle_t handle;

    result = gsl_open(gkv, NULL, &handle);
    PanicFalse(result == 0);

    result = gsl_close(handle);
    PanicFalse(result == 0);

    return result;
}

#define READ_4K (4096)
#define READ_8K (2*(READ_4K))

static uint8_t read_buffer[READ_8K];

UNITCODESECTION(KEEP)
void FlashTestBenchmarkReadAll(void)
{
    ar_fhandle fhandle = NULL;
    size_t fsize = 2;
    size_t bytes_read = 1;
    size_t offset = 0;

    ar_fopen(&fhandle, ACDB_FILENAME, AR_FOPEN_READ_ONLY);
    memset(read_buffer, 0, READ_8K);

    fsize = ar_fsize(fhandle);

    while(offset < fsize)
    {
        size_t bytes_to_read = fsize - offset > READ_4K ? READ_4K : fsize - offset;
        ar_fseek(fhandle, offset, AR_FSEEK_BEGIN);
        ar_fread(fhandle, (void*)read_buffer, bytes_to_read, &bytes_read);
        PanicFalse(bytes_to_read == bytes_read);
        offset += bytes_read;
    };

    ar_fclose(fhandle);
}

UNITCODESECTION(KEEP)
void FlashTestBenchmarkRead8k_non_seq(void)
{
    ar_fhandle fhandle = NULL;
    size_t bytes_read = 1;

    ar_fopen(&fhandle, ACDB_FILENAME, AR_FOPEN_READ_ONLY);
    memset(read_buffer, 0, READ_8K);

    ar_fread(fhandle, (void*)read_buffer, READ_4K, &bytes_read);
    ar_fseek(fhandle, 8*READ_8K+READ_4K/4, AR_FSEEK_CURRENT);
    ar_fread(fhandle, (void*)read_buffer, READ_4K, &bytes_read);

    PanicFalse(READ_4K == bytes_read);
    ar_fclose(fhandle);
}

UNITCODESECTION(KEEP)
void FlashTestBenchmarkRead(size_t bytes)
{
    ar_fhandle fhandle = NULL;
    size_t bytes_read = 1;

    ar_fopen(&fhandle, ACDB_FILENAME, AR_FOPEN_READ_ONLY);
    memset(read_buffer, 0, READ_8K);
    PanicFalse(bytes <= READ_8K);
    
    ar_fread(fhandle, (void*)read_buffer, bytes, &bytes_read);

    PanicFalse(bytes == bytes_read);
    ar_fclose(fhandle);
}

#endif /* DISABLE_TEST_API */